package com.dragons.dragonsvf.utils.video_compress;

import org.springframework.mock.web.MockMultipartFile;
import org.springframework.web.multipart.MultipartFile;
import ws.schild.jave.Encoder;
import ws.schild.jave.MultimediaObject;
import ws.schild.jave.encode.AudioAttributes;
import ws.schild.jave.encode.EncodingAttributes;
import ws.schild.jave.encode.VideoAttributes;
import ws.schild.jave.info.AudioInfo;
import ws.schild.jave.info.VideoInfo;
import ws.schild.jave.info.VideoSize;

import java.io.*;
import java.math.BigDecimal;

/**
 * @author Dragons.G
 * @date 2025-03-19 22:01:17
 * @description 使用第三方包 ws.child 对视频进行压缩
 * 该类提供基于ws.schild.jave库的视频压缩功能，支持调整视频参数如比特率、帧率、分辨率等
 * https://blog.csdn.net/SeniorShen/article/details/113246990
 */
public class VideoCompressByWs {

    /**
     * 视频压缩核心方法
     * @param source 源视频文件对象（必须为具体存在的文件）
     * @param picName 压缩后视频的文件名（不需要包含路径）
     * @return File 压缩后的视频文件对象
     *
     * 实现逻辑：
     * 1. 参数校验 和 路径准备
     * 2. 多媒体元数据解析
     * 3. 压缩必要性判断（基于文件大小和码率）
     * 4. 音频/视频编码参数配置
     * 5. 执行编码压缩
     * 6. 异常处理和资源清理
     */
    public static File compressionVideo(File source, String picName) {
        if (source == null) {
            return null;
        }

        // 构建目标文件路径：源文件目录 + 指定文件名
        String newPath = source.getAbsolutePath()
            .substring(0, source.getAbsolutePath().lastIndexOf(File.separator))
            .concat(File.separator)
            .concat(picName);
        File target = new File(newPath);

        try {
            // 创建多媒体分析对象（用于获取视频元数据）
            MultimediaObject object = new MultimediaObject(source);

            // 音频元数据获取（用于后续参数配置）
            AudioInfo audioInfo = object.getInfo().getAudio();

            /* 压缩必要性判断逻辑 */
            int maxSize = 5; // 文件大小阈值（单位MB）
            double mb = Math.ceil(source.length() / 1048576); // 计算文件大小MB
            int second = (int) object.getInfo().getDuration() / 1000; // 计算视频时长（秒）

            // 计算平均每秒码率（MB/s）并保留4位小数
            BigDecimal bd = new BigDecimal(String.format("%.4f", mb / second));
            System.out.println("开始压缩视频了--> 视频每秒平均 " + bd + " MB ");

            // 压缩条件：文件>5MB 或 码率>0.5MB/s（可根据需求调整阈值）
            boolean needCompress = mb > maxSize || bd.compareTo(new BigDecimal(0.5)) > 0;

            // 总是执行压缩（当前条件判断被注释）
            // if(needCompress){
            long time = System.currentTimeMillis();

            /* 音频编码参数配置 */
            int maxBitRate = 128000;     // 音频最大比特率（128kbps）
            int maxSamplingRate = 44100; // 音频最高采样率（44.1kHz）

            AudioAttributes audio = new AudioAttributes();
            audio.setCodec("aac"); // 设置音频编码格式为AAC

            // 动态设置音频参数（当原始参数超过阈值时限制）
            if (audioInfo.getBitRate() > maxBitRate) {
                audio.setBitRate(maxBitRate); // 限制比特率以减小文件大小
            }
            if (audioInfo.getSamplingRate() > maxSamplingRate) {
                audio.setSamplingRate(maxSamplingRate); // 限制采样率
            }

            /* 视频编码参数配置 */
            int bitRate = 800000;     // 视频目标比特率（800kbps）
            int maxFrameRate = 20;    // 最大帧率（FPS）
            int maxWidth = 1280;      // 最大水平分辨率

            VideoInfo videoInfo = object.getInfo().getVideo();
            VideoAttributes video = new VideoAttributes();
            video.setCodec("h264"); // 使用H.264视频编码

            // 动态设置视频参数
            if (videoInfo.getBitRate() > bitRate) {
                video.setBitRate(bitRate); // 限制视频比特率
            }
            if (videoInfo.getFrameRate() > maxFrameRate) {
                video.setFrameRate(maxFrameRate); // 限制帧率
            }

            // 分辨率自适应调整（保持宽高比）
            int width = videoInfo.getSize().getWidth();
            int height = videoInfo.getSize().getHeight();
            if (width > maxWidth) {
                float ratio = (float) width / maxWidth; // 计算缩放比例
                video.setSize(new VideoSize(maxWidth, (int) (height / ratio))); // 等比例调整高度
            }

            /* 编码属性整合 */
            EncodingAttributes attr = new EncodingAttributes();
            attr.setAudioAttributes(audio);
            attr.setVideoAttributes(video);

            // 创建编码器并执行转码
            Encoder encoder = new Encoder();
            encoder.encode(new MultimediaObject(source), target, attr);

            System.out.println("压缩总耗时：" + (System.currentTimeMillis() - time) / 1000 + "秒");
            return target;
            // }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 注：此处可添加压缩成功后删除源文件的逻辑
            // if(target.length() > 0){
            //     source.delete();
            // }
        }
        return source; // 压缩失败时返回原始文件
    }


    /**
     * 按比例压缩视频核心方法
     * @param source 源视频文件
     * @param targetName 输出文件名（不含路径）
     * @param compressRatio 压缩比例（15-100的整数）
     * @return 压缩后的文件对象
     *
     * 实现特性：
     * 1. 比例参数动态计算编码参数
     * 2. 智能下限保护防止过度压缩
     * 3. 自适应分辨率调整
     */
    public static File compressVideoWithRatio(File source, String targetName, int compressRatio) {
        // 参数有效性校验
        if (source == null || !source.exists()) {
            return null;
        }
        if (compressRatio <= 15 || compressRatio > 100) {
            throw new IllegalArgumentException("压缩比例需在20-100之间");
        }

        String outputPath = source.getParent() + File.separator + targetName;
        File target = new File(outputPath);

        try {
            MultimediaObject mediaObj = new MultimediaObject(source);
            AudioInfo origAudio = mediaObj.getInfo().getAudio();
            VideoInfo origVideo = mediaObj.getInfo().getVideo();

            // 计算压缩系数（反向比例：100%时为0，20%时为0.8）
            double ratioFactor = (100 - compressRatio) / 100.0;

            /* 音频参数动态配置 */
            AudioAttributes audio = new AudioAttributes();
            audio.setCodec("aac");

            // 动态比特率计算（原始值*系数，最低64kbps）
            int audioBitrate = Math.max(64000, (int)(origAudio.getBitRate() * (1 - ratioFactor*0.6)));
            audio.setBitRate(audioBitrate);

            // 动态采样率（最高保留原始值）
            audio.setSamplingRate(Math.min(origAudio.getSamplingRate(), 44100));

            /* 视频参数动态配置 */
            VideoAttributes video = new VideoAttributes();
            video.setCodec("h264");

            // 视频比特率动态计算（原始值*系数，最低500kbps）
            int videoBitrate = Math.max(500000, (int)(origVideo.getBitRate() * (1 - ratioFactor*0.7)));
            video.setBitRate(videoBitrate);

            // 帧率动态调整（最高保留原始值）
            video.setFrameRate((int) Math.min(origVideo.getFrameRate(), (int)(30 * (1 - ratioFactor*0.2))));

            // 分辨率动态调整（等比缩放）
            int origWidth = origVideo.getSize().getWidth();
            int origHeight = origVideo.getSize().getHeight();
            int targetWidth = (int)(origWidth * (1 - ratioFactor*0.3));
            if (targetWidth % 2 != 0) {
                targetWidth--; // 确保偶数分辨率
            }
            targetWidth = Math.max(640, targetWidth); // 最低640px
            float scaleRatio = (float) targetWidth / origWidth;
            video.setSize(new VideoSize(targetWidth, (int)(origHeight * scaleRatio)));

            // 封装编码属性
            EncodingAttributes encodingAttr = new EncodingAttributes();
            encodingAttr.setAudioAttributes(audio);
            encodingAttr.setVideoAttributes(video);

            // 执行编码（启用快速压缩预设）
            new Encoder().encode(mediaObj, target, encodingAttr);

            return target;
        } catch (Exception e) {
            e.printStackTrace();
            return source; // 失败时返回源文件
        }
    }




    /**
     * 视频压缩核心方法（支持MultipartFile格式）
     * @param multipartFile 源视频文件
     * @param compressRatio 压缩比例（15-100）
     * @return 压缩后的MultipartFile
     *
     * 实现特性[1,6](@ref)：
     * 1. 动态计算视频参数（比特率/分辨率/帧率）
     * 2. 保持宽高比自适应缩放
     * 3. 自动清理临时文件
     */
    public static MultipartFile compressMultipartFileVideo(MultipartFile multipartFile, int compressRatio) throws Exception {
        // 参数校验
        if (compressRatio < 15 || compressRatio > 100) {
            throw new IllegalArgumentException("压缩比例需在15-100之间");
        }

        // 创建临时文件[9,10](@ref)
        File tempInput = File.createTempFile("input_", ".mp4");
        File tempOutput = File.createTempFile("output_", ".mp4");

        try {
            // 转换MultipartFile为File[10](@ref)
            multipartFile.transferTo(tempInput);

            // 分析原始视频参数[6](@ref)
            MultimediaObject mediaObj = new MultimediaObject(tempInput);
            VideoInfo videoInfo = mediaObj.getInfo().getVideo();
            AudioInfo audioInfo = mediaObj.getInfo().getAudio();

            // 动态计算压缩参数（基于网页6建议）
            double ratioFactor = (100 - compressRatio) / 100.0;

            /* 视频参数配置 */
            VideoAttributes videoAttr = new VideoAttributes();
            videoAttr.setCodec("h264");

            // 动态比特率（原值*系数，最低500kbps）
            int videoBitrate = Math.max(500000, (int)(videoInfo.getBitRate() * (1 - ratioFactor*0.7)));
            videoAttr.setBitRate(videoBitrate);

            // 帧率调整（上限30fps）
            videoAttr.setFrameRate((int)Math.min(videoInfo.getFrameRate(), (int)(30 * (1 - ratioFactor*0.2))));

            // 分辨率自适应（网页6建议的等比缩放）
            int origWidth = videoInfo.getSize().getWidth();
            int origHeight = videoInfo.getSize().getHeight();
            int targetWidth = (int)(origWidth * (1 - ratioFactor*0.3));
            targetWidth = targetWidth % 2 == 0 ? targetWidth : targetWidth - 1; // 确保偶数
            targetWidth = Math.max(640, targetWidth);
            float scaleRatio = (float) targetWidth / origWidth;
            videoAttr.setSize(new VideoSize(targetWidth, (int)(origHeight * scaleRatio)));

            /* 音频参数配置 */
            AudioAttributes audioAttr = new AudioAttributes();
            audioAttr.setCodec("aac");
            audioAttr.setBitRate(Math.max(64000, (int)(audioInfo.getBitRate() * (1 - ratioFactor*0.6))));
            audioAttr.setSamplingRate(Math.min(audioInfo.getSamplingRate(), 44100));

            // 编码设置[1](@ref)
            EncodingAttributes encodingAttr = new EncodingAttributes();
            encodingAttr.setVideoAttributes(videoAttr);
            encodingAttr.setAudioAttributes(audioAttr);

            // 执行压缩
            new Encoder().encode(mediaObj, tempOutput, encodingAttr);

            // 转换回MultipartFile[10](@ref)
            try (InputStream is = new FileInputStream(tempOutput)) {
                return new MockMultipartFile(
                    "compressedVideo",
                    multipartFile.getOriginalFilename(),
                    "video/mp4",
                    is
                );
            }
        } finally {
            // 清理临时文件[9](@ref)
            if(tempInput.exists()) tempInput.delete();
            if(tempOutput.exists()) tempOutput.delete();
        }
    }



    /**
     * 测试用例
     * @param args 命令行参数（未使用）
     *
     * 示例：将源文件压缩为compress_done.mp4
     * 源文件路径：C:\D-Files\...\source.mp4
     * 输出文件位于同目录，名为compress_done.mp4
     */
    public static void main(String[] args) {
        String sourcePath = "C:\\D-Files\\Belife\\Black_House\\qrcode_annotation\\src\\main\\java\\com\\dragons\\video_compress\\videos\\source.mp4";
        String compressedVideoName = "compress_done.mp4";
        String compressedVideoName1 = "compress_arg_done.mp4";
        String multipartFileOutputPathb = "C:\\D-Files\\Belife\\Black_House\\qrcode_annotation\\src\\main\\java\\com\\dragons\\video_compress\\videos\\multipartFileOutputPath.mp4";
        File source = new File(sourcePath);
        //compressionVideo(source, compressedVideoName);

        // 可以设置压缩比例
        //compressVideoWithRatio(source, compressedVideoName1,15);


        // 传递MultipartFile
        // 模拟MultipartFile输入
        File testFile = new File(sourcePath);
        try (InputStream is = new FileInputStream(testFile)) {
            MultipartFile input = new MockMultipartFile(
                "video",
                testFile.getName(),
                "video/mp4",
                is
            );

            // 执行50%压缩
            MultipartFile output = compressMultipartFileVideo(input, 75);
            System.out.println("压缩完成，大小：" + output.getSize() + " bytes");

            // 测试 模拟的 MultipartyFile 压缩后的内容是否正确  这里保存到本地查看
            File outFile = new File(multipartFileOutputPathb);
            outFile.getParentFile().mkdirs(); // 确保目标文件所在的目录存在
            try(InputStream multipartFileInputStream = output.getInputStream()) {
                // 将输入流中的数据写入到目标文件
                org.apache.commons.io.FileUtils.copyInputStreamToFile(multipartFileInputStream, outFile);
            }


        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }







}